package sk.ygor.raynor.rebind.serializable.collections

import com.google.gwt.core.ext.{TreeLogger, GeneratorContext}
import sk.ygor.raynor.client.json.{JSONSerializationException, JSONSerializer}
import com.google.gwt.json.client.JSONObject
import com.google.gwt.core.ext.typeinfo.{JParameterizedType, JClassType}
import com.google.gwt.user.rebind.ClassSourceFileComposerFactory
import sk.ygor.raynor.rebind.serializable.{JSONSerializableAnalysis, CollectionTypeStructureInfo}
import sk.ygor.xtemplate.XTemplate
import sk.ygor.raynor.client.reflection.JSONTypeInfo

object JSONSerializerCollectionTypeGenerator {
  var logger: TreeLogger = null
  var context: GeneratorContext = null
  var structure: CollectionTypeStructureInfo = null

  def generate(logger: TreeLogger,
               context: GeneratorContext,
               structure: CollectionTypeStructureInfo) {
    this.logger = logger;
    this.context = context;
    this.structure = structure;

    val xtpl = new XTemplate(structure.nature match {
      case JSONTypeInfo.CollectionType.Nature.LIST => "sk/ygor/raynor/rebind/serializable/collections/JSONSerializerCollectionType_List.xtpl"
      case JSONTypeInfo.CollectionType.Nature.STRING_MAP => "sk/ygor/raynor/rebind/serializable/collections/JSONSerializerCollectionType_StringMap.xtpl"
      case JSONTypeInfo.CollectionType.Nature.ARRAY => "sk/ygor/raynor/rebind/serializable/collections/JSONSerializerCollectionType_Array.xtpl"
    })

    xtpl.assign("subSerializerClass", JSONSerializableAnalysis.getStructureInfo(structure.tail).
            serializer.fullName);
    xtpl.assign("recordType", structure.tail.getParameterizedQualifiedSourceName());
    xtpl.assign("serializerClass", structure.serializer.fullName);
    xtpl.parse();
    generateClass(xtpl.getText())
  }

  def generateClass(body: String) {
    // get print writer that receives the source code
    val printWriter = context.tryCreate(
      logger, structure.serializer.packageName, structure.serializer.simpleName
      );
    // print writer if null, source code has ALREADY been generated, return
    if (printWriter == null) {
      return;
    }

    val typeOracle = context.getTypeOracle();
    val composer = new ClassSourceFileComposerFactory(
      structure.serializer.packageName, structure.serializer.simpleName
      );

    val x = structure.nature match {
      case JSONTypeInfo.CollectionType.Nature.ARRAY => typeOracle.getArrayType(structure.tail)
      case JSONTypeInfo.CollectionType.Nature.LIST => typeOracle.getParameterizedType(
        typeOracle.getType(classOf[java.util.List[_]].getName()).isGenericType(),
        Array[JClassType](structure.tail)
        )
      case JSONTypeInfo.CollectionType.Nature.STRING_MAP => typeOracle.getParameterizedType(
        typeOracle.getType(classOf[java.util.Map[_, _]].getName()).isGenericType(),
        Array[JClassType](
          typeOracle.getType(classOf[java.lang.String].getName()),
          structure.tail
          )
        )
    }

    val interfaceType: JParameterizedType = typeOracle.getParameterizedType(
      typeOracle.getType(classOf[JSONSerializer[_]].getName()).isGenericType(),
      Array[JClassType](x)
      );

    composer.addImplementedInterface(interfaceType.getParameterizedQualifiedSourceName());
    composer.addImplementedInterface(classOf[JSONSerializer.Configuration].getName.replaceAll("\\$", "\\."))

    composer.addImport(classOf[JSONObject].getPackage().getName() + ".*");
    composer.addImport(classOf[JSONSerializationException].getPackage().getName() + ".*");
    composer.addImport(classOf[JSONSerializer.Configuration].getName().replaceAll("\\$", "\\."));
    composer.addImport("java.util.*");

    val sourceWriter = composer.createSourceWriter(context, printWriter);
    sourceWriter.println(body);

    sourceWriter.outdent();
    sourceWriter.println("}");

    context.commit(logger, printWriter);
  }


}
